﻿using System.Runtime.InteropServices;
using System.Text;

namespace Viyi.Util.Config
{
    /// <summary>
    /// 提供对INI配置文件的操作。
    /// </summary>
    /// <remarks>通过Win32 API中的<c>WritePrivateProfileString</c>
    /// 和<c>GetPrivateProfileString</c>实现。
    /// 这个类是线程不安全的。</remarks>
    public sealed class IniConfig : ReadOnlyableConfig
    {
        [DllImport("kernel32", SetLastError = true)]
        private static extern long WritePrivateProfileString(string section,
            string key, string value, string filePath);

        [DllImport("kernel32", SetLastError = true)]
        private static extern int GetPrivateProfileString(string section,
            string key, string defaultValue,
            StringBuilder retVal, int size, string filePath);

        private readonly string filename;
        private readonly StringBuilder buffer = new StringBuilder(1024);

        /// <summary>
        /// 构造一个<c>IniConfig</c>对象处理指定的配置文件。
        /// </summary>
        /// <param name="filename">INI配置文件路径</param>
        /// <param name="isReadOnly">指示该配置是否为只读，默认为false</param>
        /// <remarks>如果指定的配置文件不存在，会自动创建一个。
        /// 如果没有权限对指定的配置文件进行读写操作，抛出IOException</remarks>
        public IniConfig(string filename, bool isReadOnly = false)
        {
            this.filename = filename;
            IsReadOnly = isReadOnly;
        }

        /// <summary>
        /// 按指定的配置节和选项名称获取选项值。
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="name">选项名称</param>
        /// <returns>选项值，如果获取失败将返回<c>string.Empty</c>，而不是<c>null</c>。</returns>
        /// <remarks>如果需要转换为其它数据类型的值，
        /// 可以使用<c>string</c>扩展的<c>ToXXXX</c>系列方法。</remarks>
        public string Get(string section, string name)
        {
            int r = GetPrivateProfileString(section, name, string.Empty, buffer,
                buffer.Capacity, filename);

            return Marshal.GetLastWin32Error() == 0 ? buffer.ToString(0, r) : string.Empty;
        }

        /// <summary>
        /// 按指定的配置节和选项名称设置选项值。
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="name">选项名称</param>
        /// <param name="value">值</param>
        /// <returns>true表示设置成功，false表示设置失败</returns>
        public bool Set(string section, string name, string value)
        {
            ThrowIfReadOnly();

            WritePrivateProfileString(section, name, value, filename);
            return Marshal.GetLastWin32Error() == 0;
        }

        /// <summary>
        /// 以索引方式实现的<c>Get(string, string)</c>
        /// 和<c>Set(string, string, string)</c>
        /// 的便捷调用方式。
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="name">选项名称</param>
        /// <returns>选项值，参考<see cref="Get(string, string)"/>
        /// 和<see cref="Set(string, string, string)"/></returns>
        public string this[string section, string name]
        {
            get { return Get(section, name); }
            set { Set(section, name, value); }
        }
    }
}
